﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using Ue.CQRS.Events;
using Ue.CQRS.Utility;

namespace Ue.CQRS.DistributionManager
{
    public static class IDistributorExtensions
    {
        #region Register

        /// <summary>
        /// 拥有事件处理器的对像，实例化集合
        /// </summary>
        public static ConcurrentDictionary<Type, object> handleReflectedObjects = new ConcurrentDictionary<Type, object>();

        public static void Register(this IDistributor distributor, params Type[] types)
        {
            if (types == null || !types.Any())
            {
                return;
            }

            var methods = types.Where(type => type.IsClass)
                .SelectMany(type => type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
                .Where(method => EventUtility.EventHandleIsVerify(method))
                .Select(method => new Tuple<MethodInfo, EventHandlerAttribute>(method, GetEventHandlerAttribute(method)))
                .Where(item => item.Item2 != null)
                .ToList();

            foreach (var method in methods)
            {
                IDistributorExtensions.Register(distributor, method.Item1,method.Item2);
            }
        }
        public static void Register(this IDistributor distributor, IEnumerable<Type> types)
        {
            IDistributorExtensions.Register(distributor, types.ToArray());
        }

        /// <summary>
        /// 根据程序集注册事件处理器
        /// 会排除注册标注有EventSourceAttribute的类型
        /// </summary>
        /// <param name="distributor"></param>
        /// <param name="assembly"></param>
        public static void Register(this IDistributor distributor, Assembly assembly)
        {
            var types = assembly.GetTypes()
                .Where(type => !type.GetCustomAttributes(typeof(EventSourceAttribute), true).Any() && type.IsClass);

            IDistributorExtensions.Register(distributor, types);
        }

        private static void Register(IDistributor distributor, MethodInfo handleMethod,EventHandlerAttribute attribute)
        {
            var eventHandlerAttribute = attribute ?? GetEventHandlerAttribute(handleMethod);
            var handleMethodParameters = handleMethod.GetParameters();
            if (handleMethodParameters.Length != 1)
            {
                throw new ArgumentNullException(string.Format("事件处理器：{0}，必须有一个参数用来接收事件。",handleMethod.Name));
            }

            var handleMethodParameterIsEvent = handleMethodParameters.FirstOrDefault().ParameterType.GetInterface("IEvent") != null;
            var reflectedObject = handleReflectedObjects.GetOrAdd(handleMethod.ReflectedType, t => Activator.CreateInstance(handleMethod.ReflectedType));
            var handler = new Action<IEvent>(@event =>
            {
                handleMethod.Invoke(reflectedObject, new object[] { handleMethodParameterIsEvent ? @event : @event.EventArgs });
            });
            
            distributor.Register(eventHandlerAttribute.GetEventName(handleMethod), handler);
        }


        #endregion

        private static EventHandlerAttribute GetEventHandlerAttribute(MethodInfo method)
        {
            var attributes = method.GetCustomAttributes(typeof(EventHandlerAttribute), true);

            if (!attributes.Any())
            {
                attributes = method.ReflectedType
                   .GetInterfaces()
                   .SelectMany(item => item.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
                   .Where(item => item.Name.Equals(method.Name))
                   .SelectMany(item => item.GetCustomAttributes(typeof(EventHandlerAttribute), false))
                   .ToArray();
            }

            return attributes.Any()
                ? attributes[0] as EventHandlerAttribute
                : null;
        }
    }
}
